#include "Game.h"
#include <cstdlib> 
#include <ctime> 

//Local gui handles

#define IDC_OPEN       0
#define IDC_SAVE       1
#define IDC_CLOSE      2
#define IDC_CLOSEPOLY  3
#define IDC_MOVEPOLY   4
#define IDC_RESIZEPOLY 5
#define IDC_ROTATEPOLY 6
#define IDC_FULLSCREEN 7
#define IDC_INFO       8

#define EDITOR_DEBUG   1



void CALLBACK OnGUIEvent( UINT nEvent, int nControlID, CDXUTControl* pControl, void* pUserContext )
{
	CDXUTDialog *hud = &((Game*)pUserContext)->m_Hud;
	bool *btns       = ((Game*)pUserContext)->m_bMouse;
	
	btns[0] = false;

	switch( nControlID )
    {
        case IDC_FULLSCREEN: 
			{
				if(DXUTIsWindowed())
					hud->GetButton(IDC_FULLSCREEN)->SetText(L"Windowed");
				else
					hud->GetButton(IDC_FULLSCREEN)->SetText(L"FullScreen");

				//Changes the screen size to monitor resolution ( i will fix size issue later )
				DXUTToggleFullScreen(); 
			}
			break;
		case IDC_CLOSE:
			{
				PostQuitMessage(0);	
			}
			break;	

	}
}


bool Game::msgProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
{
	switch(uMsg) 
	{ 
		case WM_SIZE: 
			if(wParam != SIZE_MINIMIZED)
				onResize(LOWORD(lParam), HIWORD(lParam));
			break;
	}

	bool noFurtherProcessing = m_ResManager.MsgProc(hWnd, uMsg, wParam, lParam);
	if(noFurtherProcessing) return true;

	noFurtherProcessing = m_Hud.MsgProc(hWnd, uMsg, wParam, lParam);
	if(noFurtherProcessing) return true;

	return false;
}

IGame* createGame( HINSTANCE hInst, HWND hWnd )
{
	return new Game( hInst, hWnd );
}

Game::Game( HINSTANCE hInst, HWND hWnd ) : m_hWnd(hWnd), m_fov(45), 
	m_scrWidth(640), m_scrHeight(480), m_input(hInst, hWnd), m_convexSet(0)
{
	initializeEditor();
}

Game::~Game()
{
	m_Renderer.destroy();
	m_d3dDevice = 0;
}

void Game::getWindowSettings(int &width, int &height, bool &windowed, bool &minimizebox, bool &maximizebox)
{
	width       = m_scrWidth;
	height      = m_scrHeight;
	maximizebox = 1;
	windowed    = 1;
}

void Game::initializeEditor()
{
	m_Hud.Init(&m_ResManager);
	m_Hud.SetCallback(OnGUIEvent, (void*)this);

	m_cPositions[0].x = 0.0078f; m_cPositions[0].y = 0.0208f;
	m_cPositions[1].x = 0.1250f; m_cPositions[1].y = 0.0208f;
	m_cPositions[2].x = 0.2421f; m_cPositions[2].y = 0.0208f;
	m_cPositions[3].x = 0.3593f; m_cPositions[3].y = 0.0208f;
	m_cPositions[4].x = 0.4760f; m_cPositions[4].y = 0.0208f;
	m_cPositions[5].x = 0.5937f; m_cPositions[5].y = 0.0208f;
	m_cPositions[6].x = 0.7125f; m_cPositions[6].y = 0.0208f;
	m_cPositions[7].x = 0.8312f; m_cPositions[7].y = 0.0208f;


	m_Hud.AddButton(IDC_OPEN,       L"Open",       (int)(m_cPositions[0].x * m_scrWidth), (int)(m_cPositions[0].y * m_scrHeight), 70, 20);
	m_Hud.AddButton(IDC_SAVE,       L"Save",       (int)(m_cPositions[1].x * m_scrWidth), (int)(m_cPositions[1].y * m_scrHeight), 70, 20);
	m_Hud.AddButton(IDC_CLOSE,      L"Close",      (int)(m_cPositions[2].x * m_scrWidth), (int)(m_cPositions[2].y * m_scrHeight), 70, 20);
	m_Hud.AddButton(IDC_CLOSEPOLY,  L"Close Poly", (int)(m_cPositions[3].x * m_scrWidth), (int)(m_cPositions[3].y * m_scrHeight), 70, 20);
	m_Hud.AddButton(IDC_MOVEPOLY,   L"Move",       (int)(m_cPositions[4].x * m_scrWidth), (int)(m_cPositions[4].y * m_scrHeight), 70, 20);
	m_Hud.AddButton(IDC_RESIZEPOLY, L"Resize",     (int)(m_cPositions[5].x * m_scrWidth), (int)(m_cPositions[5].y * m_scrHeight), 70, 20);
	m_Hud.AddButton(IDC_ROTATEPOLY, L"Rotate",     (int)(m_cPositions[6].x * m_scrWidth), (int)(m_cPositions[6].y * m_scrHeight), 70, 20);
	m_Hud.AddButton(IDC_FULLSCREEN, DXUTIsWindowed() ? L"FullScreen" : L"Windowed", (int)(m_cPositions[7].x * m_scrWidth), 
									(int)(m_cPositions[7].y * m_scrHeight), 70, 20);

	m_Hud.SetFont(1, L"Verdana", 13, FW_REGULAR);

	m_Hud.AddStatic( IDC_INFO, L"", 15, 40, 100, 100);
	m_Hud.GetControl( IDC_INFO )->GetElement(0)->dwTextFormat = DT_LEFT|DT_TOP|DT_WORDBREAK;
	m_Hud.GetControl( IDC_INFO )->GetElement(0)->iFont        = 1;


	// initialize camera
	m_camera.hand = Camera::RIGHTHANDED;

	m_camera.eye = float3( 0, 0, 1.0f );
	m_camera.at  = float3( 0, 0, 0 );
	m_camera.up  = float3( 0, 1, 0 );
	
	m_camera.projectionType = Camera::ORTHO;
	m_camera.ortho.h = (float)m_scrHeight;
	m_camera.ortho.w = (float)m_scrWidth;


	m_camera.zn = 0.1f;
	m_camera.zf = 100.0f;

	//Set Window title
	SetWindowText(m_hWnd, L"PolyEngine Editor - Empty");

	//Clean the buffers
	ZeroMemory(m_bMouse, sizeof(bool)*3);
	ZeroMemory(m_bKeys, sizeof(bool)*256);

	//Initialize Random number
	srand((uint32)time(0));
}

void Game::onResize(int w, int h)
{
	m_scrWidth  = w;
	m_scrHeight = h;

	for(int i=0; i < BTNSIZE; i++)
		m_Hud.GetButton(i)->SetLocation((int)(m_cPositions[i].x * m_scrWidth), (int)(m_cPositions[i].y * m_scrHeight));
}

void Game::onCreateGraphics( D3DDevice device )
{
	m_d3dDevice = device;
	m_Renderer.setDevice(m_d3dDevice);

	m_d3dDevice->SetRenderState( D3DRS_CULLMODE, D3DCULL_CW );

	float44 world = identity44();
	float44 view  = m_camera.view();
	float44 proj  = m_camera.projection();

	m_d3dDevice->SetTransform( D3DTS_WORLD, &world );
	m_d3dDevice->SetTransform( D3DTS_VIEW, &view );
	m_d3dDevice->SetTransform( D3DTS_PROJECTION, &proj );

	m_ResManager.OnD3D9CreateDevice(m_d3dDevice);

}

void Game::onResetGraphics( D3DDevice device )
{
	m_d3dDevice = device;

	m_Renderer.destroy();
	m_Renderer.setDevice(m_d3dDevice);

	m_d3dDevice->SetRenderState( D3DRS_CULLMODE, D3DCULL_CW );

	m_camera.ortho.w = (float)DXUTGetD3D9BackBufferSurfaceDesc()->Width;
	m_camera.ortho.h = (float)DXUTGetD3D9BackBufferSurfaceDesc()->Height;

	float44 view = m_camera.view();
	float44 proj = m_camera.projection();

	m_d3dDevice->SetTransform( D3DTS_VIEW, &view );
	m_d3dDevice->SetTransform( D3DTS_PROJECTION, &proj );


	m_ResManager.OnD3D9ResetDevice();
	onResize(DXUTGetD3D9BackBufferSurfaceDesc()->Width, DXUTGetD3D9BackBufferSurfaceDesc()->Height);

}

void Game::onLostGraphics()
{
	m_ResManager.OnD3D9LostDevice();
}

void Game::onDestroyGraphics()
{
	m_ResManager.OnD3D9DestroyDevice();
}

void Game::handleInput()
{
	POINT cursor;
	GetCursorPos(&cursor);
	ScreenToClient(m_hWnd, &cursor);

	if(m_input.mouseState().buttons[0]) m_bMouse[0] = 1;
	if(m_input.mouseState().buttons[1]) m_bMouse[1] = 1;
	if(m_input.keyState().keys[57])     m_bKeys[57] = 1;//Space

	//Simulate mouse up
	if(m_bMouse[0] && !m_input.mouseState().buttons[0])
	{
		m_bMouse[0]  = 0;

		m_polygon.push_back(float3((float)(cursor.x - m_scrWidth/2), (float)(cursor.y  * -1 + (m_scrHeight/2)) , -1));

		if(m_polygon.size() >= 3)
		{
			m_convexSet.reset( new ConvexSet(m_polygon) );
			m_polygons = m_convexSet->polygonList();
		    //Generate Color
			if(m_colors.size() < m_polygons.size())
			{
				float3 tmp(1.0f/((rand()%10)+1), 1.0f/((rand()%10)+1), 1.0f/((rand()%10)+1));
				m_colors.push_back(tmp);
			}
		}

	}

	if(m_bMouse[1] && !m_input.mouseState().buttons[1])
	{	
		m_bMouse[1] = 0;
		m_polygon.clear();
		m_colors.clear();
	}

	//Space up
	if(m_bKeys[57] && !m_input.keyState().keys[57])
	{
		m_bKeys[57] = 0;
	}

#if EDITOR_DEBUG

	//For detecting KeyIndexes
	//int ii=-1;
	//for(int i=0;i<256;i++)
	//{
	//	if(m_input.keyState().keys[i])
	//	{
	//		ii = i;
	//		break;
	//	}
	//}

	char str[256];
	sprintf_s(str, 256,"MouseX: %d\nMouseY: %d\n", cursor.x, cursor.y);
	printScr(str);

#endif

}


void Game::onFrameMove(float dt)
{
	handleInput();
}

void Game::onFrameRender(float dt)
{

	if(m_polygon.size()>=3)
	{
		for(uint32 i = 0; i < (uint32)m_polygons.size(); i++)
		{
			m_Renderer.setup( m_polygons[i], m_colors[i] );
			m_Renderer.render(PT_TriangleFan, (uint32)m_polygons[i].size()-2);
		}
	}

	//Render Points for feedback
	if(m_polygon.size()>0)
	{
		m_Renderer.setup( m_polygon, float3( 1.0f, 1.0f, 1.0f ) );
		m_Renderer.render(PT_PointList, (uint32)m_polygon.size());
	}

	m_Hud.OnRender(dt);
}

void Game::printScr(const char *string)
{
	WCHAR   tmp[256];
	mbstowcs_s(0, tmp, 256, string, 256);
	m_Hud.GetStatic(IDC_INFO)->SetText(tmp);
}

float3 Game::screenToPickRay(float2 mousePos)
{
	D3DVIEWPORT9 viewPort;
	float44      viewMatrix,
				 matProj,
				 matWorld;

	float3  pt1, pt2;
	float3  result, diff;


	m_d3dDevice->GetTransform(D3DTS_VIEW, &viewMatrix);
	m_d3dDevice->GetTransform(D3DTS_WORLD, &matWorld);
	m_d3dDevice->GetTransform(D3DTS_PROJECTION, &matProj);

	m_d3dDevice->GetViewport( &viewPort );

	D3DXVec3Unproject( &pt1, &float3(mousePos.x, mousePos.y, 0), &viewPort, &matProj, &viewMatrix, &matWorld );
	D3DXVec3Unproject( &pt2, &float3(mousePos.x, mousePos.y, 1), &viewPort, &matProj, &viewMatrix, &matWorld );
	
	diff = pt2 - pt1;
	D3DXVec3Normalize(&result, &diff);

	return result;
}